package com.rq.shuduapp;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class Calculate {

    private String rules = null;
    private Object[][] que;
    private int tag = 0;

    public Calculate(Object[][] que, String rules) {
        if (que.length != rules.length() && que[0].length != rules.length()) {
            LOG.e("Calculate", "17:");
            return;
        }
        this.rules = rules;
        this.que = new Object[rules.length()][rules.length()];
        for (int i = 0; i < que.length; i++) {
            this.que[i] = new Object[rules.length()];
            System.arraycopy(que[i], 0, this.que[i], 0, que.length);
        }
    }

    private HashMap<String, Set<Object>> maySize = new HashMap<>();

    public Row getNext() {
        maySize.clear();
        Row[][] ques = new Row[rules.length()][rules.length()];
        for (int i = 0; i < que.length; i++) {
            for (int k = 0; k < que[i].length; k++) {
                ques[i][k] = new Row(que[i][k], i, k);
            }
        }
        for (int i = 0; i < ques.length; i++) {
            for (int m = 0; m < ques.length; m++) {
                Row item = writeOne(ques, tag);
                if (item != null) {
                    que[item.x][item.y] = item.value;
                    tag++;
                    return item;
                }
            }
            for (int m = 0; m < ques.length; m++) {
                Row item = writeTwo(ques, tag);
                if (item != null) {
                    que[item.x][item.y] = item.value;
                    tag++;
                    return item;
                }
            }
            for (int m = 0; m < ques.length; m++) {
                Row item = writeThree(ques, tag);
                if (item != null) {
                    que[item.x][item.y] = item.value;
                    ++tag;
                    return item;
                }
            }
//            Row item = guess(ques, tag);
//            if (item != null) {
//                que[item.x][item.y] = item.value;
//                ++tag;
//                return item;
//            }

        }
        return null;
    }


    /**
     * 推测产生一个
     *
     * @return
     */
    private Row guess(Row[][] ques, int tag) {
        int mayValueSize = 2;
        if (maySize != null) {
            while (mayValueSize < rules.length()) {
                for (String loc : maySize.keySet()) {
                    if (maySize.get(loc) != null && maySize.get(loc).size() == mayValueSize) {
                        int x = Integer.parseInt(loc.split(",")[0]);
                        int y = Integer.parseInt(loc.split(",")[1]);
                        Set<Object> v = maySize.get(loc);
                        Object[] canValue = v.toArray();
                        ques[x][y].value = canValue[new Random().nextInt(canValue.length)];
                        ques[x][y].writeTime = tag + 1;
                        ques[x][y].way = 4;
                        return ques[x][y];
                    }
                }
                mayValueSize++;
            }
        }
        return null;
    }

    /**
     * 数字找位置(以块为基本单位)
     *
     * @param lists
     * @return
     */
    private Row writeOne(Row[][] lists, int tag) {
        Row[] newList = new Row[rules.length() * rules.length()];
        for (int i = 0; i < lists.length; i++) {
            System.arraycopy(lists[i], 0, newList, i * rules.length(), rules.length());
        }
        for (int i = 0; i < lists.length; i++) {
            for (int k = 0; k < lists[i].length; k++) {
                Row item = checkOne(lists[i][k], lists);
                if (item != null) {
                    item.writeTime = tag + 1;
                    item.way = 1;
                    return item;
                }
            }
        }
        return null;
    }

    public Row checkOne(Row checkItem, Row[][] lists) {
        if (!isEmpty(checkItem.value)) {
            return null;
        }
        Set<Object> nonContent = new HashSet<>();
        Set<Object> mayContent = new HashSet<>();
        for (int i = 0; i < rules.length(); i++) {
            mayContent.add(rules.substring(i, i + 1));
        }
        for (int i = 0; i < lists.length; i++) {
            for (int k = 0; k < lists[i].length; k++) {
                Row item = lists[i][k];
                if (isEmpty(item.value)) continue;
                if (item.x == checkItem.x || item.y == checkItem.y || (item.sqrX == checkItem.sqrX && item.sqrY == checkItem.sqrY)) {
                    //同行.列.块内容
                    nonContent.add(String.valueOf(item.value));
                }
            }
        }
        mayContent.removeAll(nonContent);
        maySize.put(checkItem.loc(), mayContent);
        Map<Object, Set> locations = new HashMap<>();

        for (Object integer : mayContent) {
            Set<String> notLocation = new HashSet<>();
            for (int i = 0; i < lists.length; i++) {
                for (int k = 0; k < lists[i].length; k++) {
                    Row item = lists[i][k];
                    if (item.sqrX != checkItem.sqrX && item.sqrY != checkItem.sqrY) continue;
                    if (isEmpty(item.value)) continue;
                    if (checkItem.sqrX == item.sqrX && checkItem.sqrY == item.sqrY) {
                        notLocation.add("(" + item.x + "," + item.y + ")");
                        continue;//同块数据
                    }
                    if (isSame(integer, item.value)) {
                        if (item.sqrX == checkItem.sqrX) {//同行数据
                            notLocation.add("(" + item.x + "," + (int) (checkItem.sqrY * Math.sqrt(rules.length())) + ")");
                            notLocation.add("(" + item.x + "," + (int) (checkItem.sqrY * Math.sqrt(rules.length()) + 1) + ")");
                            notLocation.add("(" + item.x + "," + (int) (checkItem.sqrY * Math.sqrt(rules.length()) + 2) + ")");
                        } else {//同列数据
                            notLocation.add("(" + (int) (checkItem.sqrX * Math.sqrt(rules.length())) + "," + item.y + ")");
                            notLocation.add("(" + (int) (checkItem.sqrX * Math.sqrt(rules.length()) + 1) + "," + item.y + ")");
                            notLocation.add("(" + (int) (checkItem.sqrX * Math.sqrt(rules.length()) + 2) + "," + item.y + ")");
                        }
                    }
                }
            }
            if (locations.get(integer) == null) {
                locations.put(integer, notLocation);
            } else {
                Set<String> old = locations.get(integer);
                old.addAll(notLocation);
            }
            Set<String> mustNot = locations.get(integer);
            if (mustNot.size() == rules.length() - 1) {
                checkItem.value = integer;
                return checkItem;
            }
        }
        return null;
    }

    private void printLog(String... con) {
        if (con == null || con.length == 0) {
            System.out.println();
        } else {
            for (String s : con) {
                System.out.println(s);
            }
        }
    }


    private Row writeTwo(Row[][] lists, int tag) {
        Row[] newList = new Row[rules.length() * rules.length()];
        for (int i = 0; i < lists.length; i++) {
            System.arraycopy(lists[i], 0, newList, i * rules.length(), rules.length());
        }
        for (int i = 0; i < lists.length; i++) {
            for (int k = 0; k < lists[i].length; k++) {
                Row item = checkTwo(lists[i][k], lists);
                if (item != null) {
                    item.writeTime = tag + 1;
                    item.way = 2;
                    return item;
                }
            }
        }
        return null;
    }

    /**
     * @param lists
     * @return
     */
    public Row checkTwo(Row checkItem, Row[][] lists) {
        if (!isEmpty(checkItem.value)) {
            return null;
        }
        Set<Object> nonContent = new HashSet<>();
        Set<Object> mayContent = new HashSet<>();
        for (int i = 0; i < rules.length(); i++) {
            mayContent.add(rules.substring(i, i + 1));
        }
        for (int i = 0; i < lists.length; i++) {
            for (int k = 0; k < lists[i].length; k++) {
                Row item = lists[i][k];
                if (isEmpty(item.value)) continue;
                if (item.x == checkItem.x || item.y == checkItem.y || (item.sqrX == checkItem.sqrX && item.sqrY == checkItem.sqrY)) {
                    //同行.列.块内容
                    nonContent.add(String.valueOf(item.value));
                }
            }
        }
        mayContent.removeAll(nonContent);
        int x = checkItem.x;
        int y = checkItem.y;
        for (Object integer : mayContent) {
            Set<String> notXLocation = new HashSet<>();
            Set<String> notYLocation = new HashSet<>();
            for (int i = 0; i < rules.length(); i++) {
                if (isEmpty(lists[x][i].value)) {
                    for (int m = 0; m < rules.length(); m++) {
                        if (isSame(lists[m][i].value, integer)) {
                            notYLocation.add(lists[x][i].loc());
                            break;
                        }
                    }
                } else {
                    notYLocation.add(lists[x][i].loc());
                }
                if (isEmpty(lists[i][y].value)) {
                    for (int m = 0; m < rules.length(); m++) {
                        if (isSame(lists[i][m].value, integer)) {
                            notXLocation.add(lists[i][y].loc());
                            break;
                        }
                    }
                } else {
                    notXLocation.add(lists[i][y].loc());
                }
            }
            if (notXLocation.size() == rules.length() - 1 || notYLocation.size() == rules.length() - 1) {
                checkItem.value = integer;
                return checkItem;
            }

        }
        return null;
    }

    private Row writeThree(Row[][] ques, int tag) {
        int sqr = (int) Math.sqrt(rules.length());
        HashMap<Object, Set<String>> all = new HashMap<>();
        for (int i = 0; i < rules.length(); i++) {
            for (int k = 0; k < rules.length(); k++) {
                if (!isEmpty(ques[i][k].value)) {
                    Set<String> h;
                    if (all.keySet().contains(i)) {
                        h = all.get(i);
                    } else {
                        h = new HashSet<>();
                    }
                    h.add(String.valueOf(ques[i][k].value));
                    all.put(i, h);


                    Set<String> l;
                    if (all.keySet().contains(rules.length() + k)) {
                        l = all.get(rules.length() + k);
                    } else {
                        l = new HashSet<>();
                    }
                    l.add(String.valueOf(ques[i][k].value));
                    all.put(rules.length() + k, l);

                    int index = i / sqr * sqr + k / sqr;

                    Set<String> q;
                    if (all.keySet().contains(rules.length() * 2 + index)) {
                        q = all.get(rules.length() * 2 + index);
                    } else {
                        q = new HashSet<>();
                    }

                    q.add(String.valueOf(ques[i][k].value));
                    all.put(index + rules.length() * 2, q);
                }
            }
        }
        for (int i = 0; i < all.size(); i++) {
            Set<String> s = all.get(i);
            if (s != null && s.size() == rules.length() - 1) {
                String value = rules;
                for (String v : s) {
                    value = value.replace(v, "");
                }
                if (i < rules.length()) {
                    for (int k = 0; k < rules.length(); k++) {
                        if (isEmpty(ques[i][k].value)) {//单行只差一个
                            ques[i][k].value = value;
                            ques[i][k].writeTime = tag + 1;
                            ques[i][k].way = 31;
                            return ques[i][k];
                        }
                    }
                } else if (i < 2 * rules.length()) {
                    for (int k = 0; k < rules.length(); k++) {
                        if (isEmpty(ques[k][i - rules.length()].value)) {//单列只差一个
                            ques[k][i - rules.length()].value = value;
                            ques[k][i - rules.length()].writeTime = tag + 1;
                            ques[k][i - rules.length()].way = 32;
                            return ques[k][i - rules.length()];
                        }
                    }
                } else {
                    int index = i - rules.length() * 2;
                    for (int k = 0; k < rules.length(); k++) {
                        int x = k % sqr + index / sqr * sqr;
                        int y = k / sqr + index % sqr * sqr;
                        if (isEmpty(ques[x][y].value)) {//九宫格只差一个
                            ques[x][y].value = value;
                            ques[x][y].writeTime = tag + 1;
                            ques[x][y].way = 33;
                            return ques[x][y];
                        }
                    }
                }
            }
        }
        return null;
    }


    private boolean isSame(Object one, Object two) {
        return !isEmpty(one) && !isEmpty(two) && String.valueOf(one).equals(String.valueOf(two));
    }

    public boolean isEmpty(Object con) {
        try {
            return con == null
                    || con instanceof Integer && (int) con == 0
                    || con instanceof String && !rules.contains((String) con)
                    || con instanceof CharSequence && !rules.contains((CharSequence) con)
                    || con instanceof Character && !rules.contains(String.valueOf(con));


        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(rules);
            System.out.println(con);
            return true;
        }
    }

    /**
     * @return 验证结果是否正确
     */
    public static boolean isOk(Object[][] con, final String rules) {
        if (con != null) {
            for (int x = 0; x < con.length; x++) {
                for (int y = 0; y < con[x].length; y++) {
                    if (Calculate2.isEmpty(con[x][y], rules)) {
                        return false;
                    }
                }
            }
            for (int i = 0; i < con.length; i++) {
                Set<String> res = new HashSet<>();
                for (int k = 0; k < con[i].length; k++) {
                    String c;
                    if (con[i][k] instanceof Row) {
                        c = String.valueOf(((Row) con[i][k]).value);
                    } else {
                        c = String.valueOf(con[i][k]);
                    }
                    if (rules.contains(c)) res.add(c);

                }
                if (res.size() != rules.length()) {
//                    printLog("error-H" + (i + 1) + ">>" + res);
                    return false;
                }
            }
            for (int i = 0; i < con.length; i++) {
                Set<String> res = new HashSet<>();
                for (int k = 0; k < con[i].length; k++) {
                    String c;
                    if (con[k][i] instanceof Row) {
                        c = String.valueOf(((Row) con[k][i]).value);
                    } else {
                        c = String.valueOf(con[k][i]);
                    }
                    if (rules.contains(c)) res.add(c);

                }
                if (res.size() != rules.length()) {
//                    printLog("error-L" + (i + 1));
                    return false;
                }
            }
            int l = (int) Math.sqrt(rules.length());

            for (int outI = 0; outI < l; outI++) {
                for (int outK = 0; outK < l; outK++) {
                    Set<String> res = new HashSet<>();
                    for (int i = 0; i < l; i++) {
                        for (int k = 0; k < l; k++) {
                            String c;
                            if (con[i + outI * l][k + outK * l] instanceof Row) {
                                c = String.valueOf(((Row) con[i + outI * l][k + outK * l]).value);
                            } else {
                                c = String.valueOf(con[i + outI * l][k + outK * l]);
                            }
                            if (rules.contains(c)) res.add(c);
                        }
                    }
                    if (res.size() != rules.length()) {
//                    printLog("error-K" + (i + 1));
                        return false;
                    }

                }
            }
        }
        return true;
    }

}
